package com.example.mq;

import com.alibaba.fastjson2.JSONObject;
import com.example.entity.SecKillSku;
import com.example.mapper.SkuMapper;
import com.example.service.OrderService;
import com.example.vo.SecKillMessageVO;
import com.rabbitmq.client.Channel;
import io.netty.handler.ssl.OpenSsl;
import jakarta.annotation.Resource;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import java.util.Objects;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/**
 * @Author 游家纨绔
 * @Description TODO
 * @Create 2023-09-05 22:30
 */
@Component
public class SecKillMQ {

	@Resource
	private RedisTemplate<String, Integer> redisTemplate;
	@Autowired
	private OrderService orderService;
	@Autowired
	private SkuMapper skuMapper;

	@RabbitListener(
			bindings = @QueueBinding(
					exchange = @Exchange(value = "secKillExchange"),
					value = @Queue(value = "secKillQueue"),
					key = "secKillKey"
			),
			concurrency = "16"  // 每个节点消费者数量
	)
	public void listener(String msgVO, List<Message> messages, Channel channel) {
		try {
			SecKillMessageVO secKillMessageVO = JSONObject.parseObject(msgVO, SecKillMessageVO.class);
			String tag = MessageFormat.format("userId:{0}:{1}", secKillMessageVO.getUserId(), secKillMessageVO.getSkuId());
			SecKillSku secKillSku = skuMapper.selectOne(SecKillSku.builder().id(secKillMessageVO.getSkuId()).build());
			// 没找到商品
			// if (ObjectUtils.isEmpty(secKillSku)) {
			//     return;
			// }
			// 判断商品是否还有库存
			if (Objects.isNull(secKillSku) || secKillSku.getStock() < 1) {
				System.out.println("在mq中被判断没找到商品或库存<1");
				return;
			}
			// 判断消息是否重复消费（避免第一个请求还未设置消费标识，第二个请求就已经通过预校验加入mq队列）
			if (redisTemplate.hasKey(tag)) {
				System.out.println(tag + "在mq中被判断出重复消费");
				return;
			}
			// 创建订单
			orderService.extracted(secKillMessageVO, secKillSku, tag);
			// TODO 调用支付模块...... 可以通过 MQ 往支付模块发送消息【但是又会出现数据丢失问题，接着就是补偿问题，这里感觉可以优化下】
			channel.basicAck(messages.getLast().getMessageProperties().getDeliveryTag(), false);
		} catch (Exception e) {
			try {
				channel.basicReject(messages.getLast().getMessageProperties().getDeliveryTag(), false);
				// TODO 这里手动拒绝消息消息消费，可在数据库中和Redis 中添加库存。。。。。。
				// redisTemplate.opsForValue().increment(""+sku.getId(), 1); 这里通过原子操作进行库存补偿
				// 还可以扩展一下思路：
				//    总体限制：一名用户最多限制购买 一件/n件 商品
				//    局部限制：一次订单最多支持下单 一件/n件 商品
			} catch (IOException ex) {
				throw new RuntimeException(ex);
			}
		}
	}

}
